#!/usr/bin/python3
# -*- coding: utf-8 -*-
# cython: language_level=3

import os, re, shutil, platform, datetime, sys, zipfile, copy

if platform.system() == 'Windows':
    # 以下两行为解决问题: 致命错误 C1002: 第 2 遍编译器的堆空间不足
    from distutils import _msvccompiler
    _msvccompiler.PLAT_TO_VCVARS['win-amd64'] = 'amd64'

    from setuptools import setup
    from Cython.Build import cythonize

import logging
from logging import handlers as logging_handles
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

th = logging.StreamHandler() # 输出到屏幕上
th.setFormatter(logging.Formatter("[%(asctime)s] [%(levelname)s] [%(module)s] [%(funcName)s] [%(lineno)s] %(message)s")) # 设置文件里写入的格式
logger.addHandler(th)

work_dir = os.path.split(os.path.abspath(__file__))[0]
if len(work_dir) == 0: work_dir = os.path.abspath(".")
# logger.info("work dir(abspath): %s" % work_dir)

def printHelp():
    print("Usage:")
    print("发布流程: 锁定仓库, 编译代码, 打包文件, 加密文件, 版本发布, 解锁仓库")
    print("python build.py -help")
    print("python build.py -lockRepository                # 锁定仓库, 打包前执行")
    print("python build.py -compile 3.6|3.7|3.8           # 以当前版本编译所有文件")
    print("python build.py -compile 3.6|3.7|3.8 XXX.py    # 以当前版本编译指定文件")
    print("python build.py -package ZJ300-CS3             # 打包 ZJ300-CS3")
    print("python build.py -package ZJ300-CS3_Report      # 打包 ZJ300-CS3 报告软件")
    print("python build.py -package ZJ-HST                # 打包 ZJ-HST")
    print("python build.py -package ZJSlideViewer         # 打包 ZJSlideViewer")
    print("python build.py -package ZJELISPOT             # 打包 ZJELISPOT")
    print("python build.py -encrypt ZJ300-CS3             # 加密 ZJ300-CS3")
    print("python build.py -encrypt ZJ300-CS3_Report      # 加密 ZJ300-CS3 报告软件")
    print("python build.py -encrypt ZJ-HST                # 加密 ZJ-HST")
    print("python build.py -encrypt ZJSlideViewer         # 加密 ZJSlideViewer")
    print("python build.py -encrypt ZJScan                # 加密 ZJScan")
    print("python build.py -encrypt ZJELISPOT             # 加密 ZJELISPOT")
    print("python build.py -release ZJ300-CS3             # 发布 ZJ300-CS3")
    print("python build.py -release ZJ300-CS3_Report      # 发布 ZJ300-CS3 报告软件")
    print("python build.py -release ZJ-HST                # 发布 ZJ-HST")
    print("python build.py -release ZJSlideViewer         # 发布 ZJSlideViewer")
    print("python build.py -release ZJELISPOT             # 发布 ZJELISPOT")
    print("python build.py -unlockRepository              # 解锁仓库, 所有打包均完成后提交")

def compilePy(PY_VER_FULL, PY_VER_FULL_NODOT, lst_file):
    lst_file_all = [
            "cfgZJELISPOT.py",
            "database.py",
            "common.py",
            "languageTrans.py",
            "pageScanELISPOT.py",
            "pageQC.py",
            "pageResult.py",
            "pageSetting.py",
            "pageDeviceQC.py",
            "pageDataManager.py",
            "qcCommon.py",
            "ZJELISPOT.py",
            # "reportZJELISPOT", # 以源码形式发布以便于修改
            "reportViewer.py",
            "srvc_scan.py",
            "srvc_dgns.py",
            "zjElispotView.py",
            "zjGraphView.py",
            "zjLogger.py",
            "zjMacro.py",
            "zjRoute.py",
            "zjSrvc.py",
            "zjDef.py",
            "zjTar.py",
        ]
    for file_full_name in ["zjViewer.py", "zjPyramid.py", ]:
        if os.path.isfile(os.path.join(work_dir, file_full_name)):
            lst_file_all.append(file_full_name)

    if lst_file is None: lst_file = lst_file_all
    lst_file = [f for f in lst_file if os.path.isfile(f)]

    # 删除历史残留目标文件
    for file_full_name in lst_file:
        file_name = os.path.splitext(file_full_name)[0]
        if platform.system() == 'Windows':
            file_pth = os.path.join(work_dir, "%s.cp%s-win_amd64.pyd" % (file_name, PY_VER_FULL_NODOT))
        else:
            file_pth = os.path.join(work_dir, "%s.so" % (file_name))

        if os.path.isfile(file_pth): os.remove(file_pth)

    # 删除历史残留编译中间文件
    for file_full_name in lst_file:
        file_name = os.path.splitext(file_full_name)[0]
        file_pth = os.path.join(work_dir, file_name + ".c")
        if os.path.isfile(file_pth): os.remove(file_pth)
        file_pth = os.path.join(work_dir, file_name + ".o")
        if os.path.isfile(file_pth): os.remove(file_pth)

    # 编译目标文件
    if platform.system() == 'Windows':
        argv_old = copy.deepcopy(sys.argv) # 备份原来的命令行参数
        argv_new = []
        argv_new.append(sys.argv[0])
        argv_new.append("build_ext")
        argv_new.append("--inplace")
        sys.argv = argv_new # 设置为所需的新的命令行参数

        for file_full_name in lst_file:
            setup(ext_modules = cythonize(file_full_name))

        sys.argv = argv_old # 恢复命令行参数
    else:
        for file_full_name in lst_file:
            file_name = os.path.splitext(file_full_name)[0]

            str_cmd = "cython -%s %s/%s.py" % (PY_VER_FULL[0], work_dir, file_name)
            logger.debug(str_cmd)
            os.system(str_cmd)

            str_cmd = "gcc -c -fPIC -I/usr/include/python%s %s/%s.c -o %s/%s.o" \
                    % (PY_VER_FULL, work_dir, file_name, work_dir, file_name)
            logger.debug(str_cmd)
            os.system(str_cmd)

            str_cmd = "gcc -shared %s/%s.o -o %s/%s.so" % (work_dir, file_name, work_dir, file_name)
            logger.debug(str_cmd)
            os.system(str_cmd)

            str_cmd = "strip %s/%s.so" % (work_dir, file_name)
            logger.debug(str_cmd)
            os.system(str_cmd)

    # 删除编译中间文件
    for file_full_name in lst_file:
        file_name = os.path.splitext(file_full_name)[0]
        file_pth = os.path.join(work_dir, file_name + ".c")
        if os.path.isfile(file_pth): os.remove(file_pth)

        file_pth = os.path.join(work_dir, file_name + ".o")
        if os.path.isfile(file_pth): os.remove(file_pth)

    # 移除 pyX.Y 目录下历史目标文件, 将新文件移动到 pyX.Y 目录下
    pyver_dir = os.path.join(work_dir, "py%s" % PY_VER_FULL)
    if not os.path.exists(pyver_dir):
        os.mkdir(pyver_dir)
    for file_full_name in lst_file:
        file_name = os.path.splitext(file_full_name)[0]
        if platform.system() == 'Windows':
            file_pth_trg = os.path.join(pyver_dir, "%s.pyd" % file_name)
        else:
            file_pth_trg = os.path.join(pyver_dir, "%s.so" % file_name)
        if os.path.isfile(file_pth_trg): os.remove(file_pth_trg)

        if platform.system() == 'Windows':
            file_pth_src = os.path.join(work_dir, "%s.cp%s-win_amd64.pyd" % (file_name, PY_VER_FULL_NODOT))
        else:
            file_pth_src = os.path.join(work_dir, "%s.so" % (file_name))
        if os.path.isfile(file_pth_src): shutil.move(file_pth_src, file_pth_trg)

    # 移除编译临时文件
    if platform.system() == 'Windows':
        build_dir = os.path.join(work_dir, "build")
        if os.path.isdir(build_dir): shutil.rmtree(build_dir)

def getSvnVersion(): # 获取版本号, 失败则返回 None
    strSvnVersion = None
    t = os.popen('svn info')
    var = t.read()
    svnInfoList = var.splitlines()
    for svnInfo in svnInfoList:
        if svnInfo.find('Revision:') >= 0 or svnInfo.find('版本:') >= 0:
            #version = re.findall("\d+", str)
            svnInfo = svnInfo.split(":")[1]
            strSvnVersion = svnInfo.strip()
            break
    return strSvnVersion

def lockRepository():
    isRelease = True

    strSvnVersion = getSvnVersion()
    if strSvnVersion is None:
        logger.error("获取 svn 版本号失败")
        return False

    strSvnVersion = str(int(strSvnVersion) + 1) # 版本号 +1

    if True: # 修改 cfgZJ300.py 文件
        strVersion = "    PRODUCT_VERSION = \"1.0.0.{}\"\n".format(strSvnVersion)
        strRelease = "    PRODUCT_IS_RELEASE = {}\n".format("True" if isRelease else "False")
        cfgFile = "cfgZJELISPOT.py"
        txtList = []
        with open(cfgFile, "r", encoding="utf-8") as f:
            txtList = f.readlines()
            for indx, txtLine in enumerate(txtList):
                if txtLine.find("PRODUCT_VERSION = ") >= 0:
                    txtList[indx] = strVersion
                if txtLine.find("PRODUCT_IS_RELEASE = ") >= 0:
                    txtList[indx] = strRelease
            f.close()
        with open(cfgFile, "w", encoding="utf-8") as f:
            f.writelines(txtList)
            f.close()

def unlockRepository():
    strSvnVersion = getSvnVersion()
    if strSvnVersion is None:
        logger.error("获取 svn 版本号失败")
        return False

    strSvnVersion = str(int(strSvnVersion) + 1) # 版本号 +1

    str_cmd = "svn commit -m '[feat] 提交打包版本号为 {}'".format(strSvnVersion)
    logger.debug(str_cmd)
    os.system(str_cmd)

def zipDirToZip(dirname, zipfilename):
    filelist = []
    if os.path.isfile(dirname):
        filelist.append(dirname)
    else:
        for root, dirs, files in os.walk(dirname):
            for name in files:
                filelist.append(os.path.join(root, name))

    zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED)
    for tar in filelist:
        arcname = tar[len(dirname):]
        zf.write(tar,arcname)
    zf.close()

def package(strPackageType):
    dct_package = {
            ("ZJ300-CS3", "Linux"): [
                ("run_ZJ300-CS3.py",            "run_ZJ300-CS3.py"),
                ("reportZJ300.py",              "reportZJ300.py"),
                ("zjCalendar.py",               "zjCalendar.py"),
                ("ZJ300-CS3.desktop",           "ZJ300-CS3.desktop"),
                ("install.py",                  "install.py"),
                ("loc.json",                    "loc.json"),
                ("SimHei.ttf",                  "SimHei.ttf"),
                ("ZJ300-CS3.sql",               "ZJ300-CS3.sql"),
                ("resource",                    "resource"),
                ("qss/pageCfg.qss",             "qss/pageCfg.qss"),
                ("qss/pageDiagnose.qss",        "qss/pageDiagnose.qss"),
                ("qss/pagePatientInf.qss",      "qss/pagePatientInf.qss"),
                ("qss/pageScanHST.qss",         "qss/pageScanHST.qss"),
                ("qss/pageScan.qss",            "qss/pageScan.qss"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/pageStatistic.qss",       "qss/pageStatistic.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),
                ("py3.6/cfgZJ300.so",           "py3.6/cfgZJ300.so"),
                ("py3.6/pageCfg.so",            "py3.6/pageCfg.so"),
                ("py3.6/pagePatientInf.so",     "py3.6/pagePatientInf.so"),
                ("py3.6/pageScan.so",           "py3.6/pageScan.so"),
                ("py3.6/pageStatistic.so",      "py3.6/pageStatistic.so"),
                ("py3.6/zjWidget.so",           "py3.6/zjWidget.so"),
                ("py3.6/database.so",           "py3.6/database.so"),
                ("py3.6/pageDiagnose.so",       "py3.6/pageDiagnose.so"),
                ("py3.6/pageSliceList.so",      "py3.6/pageSliceList.so"),
                ("py3.6/scanTemplate.so",       "py3.6/scanTemplate.so"),
                ("py3.6/sliceViewer.so",        "py3.6/sliceViewer.so"),
                ("py3.6/ZJ300.so",              "py3.6/ZJ300.so"),
                ("py3.6/pageScanHST.so",        "py3.6/pageScanHST.so"),

                ("py3.7/cfgZJ300.so",           "py3.7/cfgZJ300.so"),
                ("py3.7/pageCfg.so",            "py3.7/pageCfg.so"),
                ("py3.7/pagePatientInf.so",     "py3.7/pagePatientInf.so"),
                ("py3.7/pageScan.so",           "py3.7/pageScan.so"),
                ("py3.7/pageStatistic.so",      "py3.7/pageStatistic.so"),
                ("py3.7/zjWidget.so",           "py3.7/zjWidget.so"),
                ("py3.7/database.so",           "py3.7/database.so"),
                ("py3.7/pageDiagnose.so",       "py3.7/pageDiagnose.so"),
                ("py3.7/pageSliceList.so",      "py3.7/pageSliceList.so"),
                ("py3.7/scanTemplate.so",       "py3.7/scanTemplate.so"),
                ("py3.7/sliceViewer.so",        "py3.7/sliceViewer.so"),
                ("py3.7/ZJ300.so",              "py3.7/ZJ300.so"),
                ("py3.7/pageScanHST.so",        "py3.7/pageScanHST.so"),

                ("py3.8/cfgZJ300.so",           "py3.8/cfgZJ300.so"),
                ("py3.8/pageCfg.so",            "py3.8/pageCfg.so"),
                ("py3.8/pagePatientInf.so",     "py3.8/pagePatientInf.so"),
                ("py3.8/pageScan.so",           "py3.8/pageScan.so"),
                ("py3.8/pageStatistic.so",      "py3.8/pageStatistic.so"),
                ("py3.8/zjWidget.so",           "py3.8/zjWidget.so"),
                ("py3.8/database.so",           "py3.8/database.so"),
                ("py3.8/pageDiagnose.so",       "py3.8/pageDiagnose.so"),
                ("py3.8/pageSliceList.so",      "py3.8/pageSliceList.so"),
                ("py3.8/scanTemplate.so",       "py3.8/scanTemplate.so"),
                ("py3.8/sliceViewer.so",        "py3.8/sliceViewer.so"),
                ("py3.8/ZJ300.so",              "py3.8/ZJ300.so"),
                ("py3.8/pageScanHST.so",        "py3.8/pageScanHST.so"),
                ],
            # ("ZJ300-CS3", "Windows"): [], # 不支持
            ("ZJ300-CS3_Report", "Linux"): [
                ("run_ZJ300-CS3_Report.py",     "run_ZJ300-CS3_Report.py"),
                ("ZJ300-CS3_Report.desktop",    "ZJ300-CS3_Report.desktop"),
                ("reportZJ300.py",              "reportZJ300.py"),
                ("zjCalendar.py",               "zjCalendar.py"),
                ("install.py",                  "install.py"),
                ("loc.json",                    "loc.json"),
                ("SimHei.ttf",                  "SimHei.ttf"),
                ("resource",                    "resource"),
                ("qss/pageCfg.qss",             "qss/pageCfg.qss"),
                ("qss/pageDiagnose.qss",        "qss/pageDiagnose.qss"),
                ("qss/pagePatientInf.qss",      "qss/pagePatientInf.qss"),
                ("qss/pageScanHST.qss",         "qss/pageScanHST.qss"),
                ("qss/pageScan.qss",            "qss/pageScan.qss"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/pageStatistic.qss",       "qss/pageStatistic.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),
                ("py3.6/cfgZJ300.so",           "py3.6/cfgZJ300.so"),
                ("py3.6/pageCfg.so",            "py3.6/pageCfg.so"),
                ("py3.6/pagePatientInf.so",     "py3.6/pagePatientInf.so"),
                ("py3.6/pageScan.so",           "py3.6/pageScan.so"),
                ("py3.6/pageStatistic.so",      "py3.6/pageStatistic.so"),
                ("py3.6/zjWidget.so",           "py3.6/zjWidget.so"),
                ("py3.6/database.so",           "py3.6/database.so"),
                ("py3.6/pageDiagnose.so",       "py3.6/pageDiagnose.so"),
                ("py3.6/pageSliceList.so",      "py3.6/pageSliceList.so"),
                ("py3.6/scanTemplate.so",       "py3.6/scanTemplate.so"),
                ("py3.6/sliceViewer.so",        "py3.6/sliceViewer.so"),
                ("py3.6/ZJ300.so",              "py3.6/ZJ300.so"),
                ("py3.6/pageScanHST.so",        "py3.6/pageScanHST.so"),
                ("py3.6/srvc_scan.so",          "py3.6/srvc_scan.so"),
                ("py3.6/srvc_dgns.so",          "py3.6/srvc_dgns.so"),
                ("py3.6/zjMacro.so",            "py3.6/zjMacro.so"),
                ("py3.6/zjViewer.so",           "py3.6/zjViewer.so"),
                ("py3.6/zjPyramid.so",          "py3.6/zjPyramid.so"),
                ("py3.6/zjDef.so",              "py3.6/zjDef.so"),
                ("py3.6/zjTar.so",              "py3.6/zjTar.so"),

                ("py3.7/cfgZJ300.so",           "py3.7/cfgZJ300.so"),
                ("py3.7/pageCfg.so",            "py3.7/pageCfg.so"),
                ("py3.7/pagePatientInf.so",     "py3.7/pagePatientInf.so"),
                ("py3.7/pageScan.so",           "py3.7/pageScan.so"),
                ("py3.7/pageStatistic.so",      "py3.7/pageStatistic.so"),
                ("py3.7/zjWidget.so",           "py3.7/zjWidget.so"),
                ("py3.7/database.so",           "py3.7/database.so"),
                ("py3.7/pageDiagnose.so",       "py3.7/pageDiagnose.so"),
                ("py3.7/pageSliceList.so",      "py3.7/pageSliceList.so"),
                ("py3.7/scanTemplate.so",       "py3.7/scanTemplate.so"),
                ("py3.7/sliceViewer.so",        "py3.7/sliceViewer.so"),
                ("py3.7/ZJ300.so",              "py3.7/ZJ300.so"),
                ("py3.7/pageScanHST.so",        "py3.7/pageScanHST.so"),
                ("py3.7/srvc_scan.so",          "py3.7/srvc_scan.so"),
                ("py3.7/srvc_dgns.so",          "py3.7/srvc_dgns.so"),
                ("py3.7/zjMacro.so",            "py3.7/zjMacro.so"),
                ("py3.7/zjViewer.so",           "py3.7/zjViewer.so"),
                ("py3.7/zjPyramid.so",          "py3.7/zjPyramid.so"),
                ("py3.7/zjDef.so",              "py3.7/zjDef.so"),
                ("py3.7/zjTar.so",              "py3.7/zjTar.so"),

                ("py3.8/cfgZJ300.so",           "py3.8/cfgZJ300.so"),
                ("py3.8/pageCfg.so",            "py3.8/pageCfg.so"),
                ("py3.8/pagePatientInf.so",     "py3.8/pagePatientInf.so"),
                ("py3.8/pageScan.so",           "py3.8/pageScan.so"),
                ("py3.8/pageStatistic.so",      "py3.8/pageStatistic.so"),
                ("py3.8/zjWidget.so",           "py3.8/zjWidget.so"),
                ("py3.8/database.so",           "py3.8/database.so"),
                ("py3.8/pageDiagnose.so",       "py3.8/pageDiagnose.so"),
                ("py3.8/pageSliceList.so",      "py3.8/pageSliceList.so"),
                ("py3.8/scanTemplate.so",       "py3.8/scanTemplate.so"),
                ("py3.8/sliceViewer.so",        "py3.8/sliceViewer.so"),
                ("py3.8/ZJ300.so",              "py3.8/ZJ300.so"),
                ("py3.8/pageScanHST.so",        "py3.8/pageScanHST.so"),
                ("py3.8/srvc_scan.so",          "py3.8/srvc_scan.so"),
                ("py3.8/srvc_dgns.so",          "py3.8/srvc_dgns.so"),
                ("py3.8/zjMacro.so",            "py3.8/zjMacro.so"),
                ("py3.8/zjViewer.so",           "py3.8/zjViewer.so"),
                ("py3.8/zjPyramid.so",          "py3.8/zjPyramid.so"),
                ("py3.8/zjDef.so",              "py3.8/zjDef.so"),
                ("py3.8/zjTar.so",              "py3.8/zjTar.so"),
                ],
            ("ZJ300-CS3_Report", "Windows"): [
                ("run_ZJ300-CS3_Report.py",     "run_ZJ300-CS3_Report.py"),
                ("reportZJ300.py",              "reportZJ300.py"),
                ("zjCalendar.py",               "zjCalendar.py"),
                ("loc.json",                    "loc.json"),
                ("SimHei.ttf",                  "SimHei.ttf"),
                ("resource",                    "resource"),
                ("lib/wkhtmltopdf",             "wkhtmltopdf"),
                ("lib/poppler",                 "poppler"),
                ("qss/pageCfg.qss",             "qss/pageCfg.qss"),
                ("qss/pageDiagnose.qss",        "qss/pageDiagnose.qss"),
                ("qss/pagePatientInf.qss",      "qss/pagePatientInf.qss"),
                ("qss/pageScanHST.qss",         "qss/pageScanHST.qss"),
                ("qss/pageScan.qss",            "qss/pageScan.qss"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/pageStatistic.qss",       "qss/pageStatistic.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),

                ("py3.8/zjLogger.pyd",          "zjLogger.pyd"),
                ("py3.8/cfgZJ300.pyd",          "cfgZJ300.pyd"),
                ("py3.8/pageCfg.pyd",           "pageCfg.pyd"),
                ("py3.8/pagePatientInf.pyd",    "pagePatientInf.pyd"),
                ("py3.8/pageScan.pyd",          "pageScan.pyd"),
                ("py3.8/pageStatistic.pyd",     "pageStatistic.pyd"),
                ("py3.8/zjWidget.pyd",          "zjWidget.pyd"),
                ("py3.8/database.pyd",          "database.pyd"),
                ("py3.8/pageDiagnose.pyd",      "pageDiagnose.pyd"),
                ("py3.8/pageSliceList.pyd",     "pageSliceList.pyd"),
                ("py3.8/scanTemplate.pyd",      "scanTemplate.pyd"),
                ("py3.8/sliceViewer.pyd",       "sliceViewer.pyd"),
                ("py3.8/ZJ300.pyd",             "ZJ300.pyd"),
                ("py3.8/pageScanHST.pyd",       "pageScanHST.pyd"),
                ("py3.8/srvc_scan.pyd",         "srvc_scan.pyd"),
                ("py3.8/srvc_dgns.pyd",         "srvc_dgns.pyd"),
                ("py3.8/zjViewer.pyd",          "zjViewer.pyd"),
                ("py3.8/zjPyramid.pyd",         "zjPyramid.pyd"),
                ("py3.8/zjMacro.pyd",           "zjMacro.pyd"),
                ("py3.8/zjDef.pyd",             "zjDef.pyd"),
                ("py3.8/zjTar.pyd",             "zjTar.pyd"),
                ],
            ("ZJ-HST", "Linux"): [
                ("run_ZJ-HST.py",               "run_ZJ-HST.py"),
                ("reportZJ300.py",              "reportZJ300.py"),
                ("zjCalendar.py",               "zjCalendar.py"),
                ("install.py",                  "install.py"),
                ("ZJ-HST.desktop",              "ZJ-HST.desktop"),
                ("loc.json",                    "loc.json"),
                ("SimHei.ttf",                  "SimHei.ttf"),
                ("resource",                    "resource"),
                ("qss/pageCfg.qss",             "qss/pageCfg.qss"),
                ("qss/pageDiagnose.qss",        "qss/pageDiagnose.qss"),
                ("qss/pagePatientInf.qss",      "qss/pagePatientInf.qss"),
                ("qss/pageScanHST.qss",         "qss/pageScanHST.qss"),
                ("qss/pageScan.qss",            "qss/pageScan.qss"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/pageStatistic.qss",       "qss/pageStatistic.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),
                ("py3.6/cfgZJ300.so",           "py3.6/cfgZJ300.so"),
                ("py3.6/pageCfg.so",            "py3.6/pageCfg.so"),
                ("py3.6/pagePatientInf.so",     "py3.6/pagePatientInf.so"),
                ("py3.6/pageScan.so",           "py3.6/pageScan.so"),
                ("py3.6/pageStatistic.so",      "py3.6/pageStatistic.so"),
                ("py3.6/zjWidget.so",           "py3.6/zjWidget.so"),
                ("py3.6/database.so",           "py3.6/database.so"),
                ("py3.6/pageDiagnose.so",       "py3.6/pageDiagnose.so"),
                ("py3.6/pageSliceList.so",      "py3.6/pageSliceList.so"),
                ("py3.6/scanTemplate.so",       "py3.6/scanTemplate.so"),
                ("py3.6/sliceViewer.so",        "py3.6/sliceViewer.so"),
                ("py3.6/ZJ300.so",              "py3.6/ZJ300.so"),
                ("py3.6/pageScanHST.so",        "py3.6/pageScanHST.so"),

                ("py3.7/cfgZJ300.so",           "py3.7/cfgZJ300.so"),
                ("py3.7/pageCfg.so",            "py3.7/pageCfg.so"),
                ("py3.7/pagePatientInf.so",     "py3.7/pagePatientInf.so"),
                ("py3.7/pageScan.so",           "py3.7/pageScan.so"),
                ("py3.7/pageStatistic.so",      "py3.7/pageStatistic.so"),
                ("py3.7/zjWidget.so",           "py3.7/zjWidget.so"),
                ("py3.7/database.so",           "py3.7/database.so"),
                ("py3.7/pageDiagnose.so",       "py3.7/pageDiagnose.so"),
                ("py3.7/pageSliceList.so",      "py3.7/pageSliceList.so"),
                ("py3.7/scanTemplate.so",       "py3.7/scanTemplate.so"),
                ("py3.7/sliceViewer.so",        "py3.7/sliceViewer.so"),
                ("py3.7/ZJ300.so",              "py3.7/ZJ300.so"),
                ("py3.7/pageScanHST.so",        "py3.7/pageScanHST.so"),

                ("py3.8/cfgZJ300.so",           "py3.8/cfgZJ300.so"),
                ("py3.8/pageCfg.so",            "py3.8/pageCfg.so"),
                ("py3.8/pagePatientInf.so",     "py3.8/pagePatientInf.so"),
                ("py3.8/pageScan.so",           "py3.8/pageScan.so"),
                ("py3.8/pageStatistic.so",      "py3.8/pageStatistic.so"),
                ("py3.8/zjWidget.so",           "py3.8/zjWidget.so"),
                ("py3.8/database.so",           "py3.8/database.so"),
                ("py3.8/pageDiagnose.so",       "py3.8/pageDiagnose.so"),
                ("py3.8/pageSliceList.so",      "py3.8/pageSliceList.so"),
                ("py3.8/scanTemplate.so",       "py3.8/scanTemplate.so"),
                ("py3.8/sliceViewer.so",        "py3.8/sliceViewer.so"),
                ("py3.8/ZJ300.so",              "py3.8/ZJ300.so"),
                ("py3.8/pageScanHST.so",        "py3.8/pageScanHST.so"),
                ],
            # ("ZJ-HST", "Windows"): [], # 不支持
            ("ZJSlideViewer", "Linux"): [
                ("run_ZJSlideViewer.py",        "run_ZJSlideViewer.py"),
                ("ZJSlideViewer.desktop",       "ZJSlideViewer.desktop"),
                ("install.py",                  "install.py"),
                ("loc.json",                    "loc.json"),
                ("resource",                    "resource"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),
                ("py3.6/cfgZJ300.so",           "py3.6/cfgZJ300.so"),
                ("py3.6/srvc_dgns.so",          "py3.6/srvc_dgns.so"),
                ("py3.6/zjWidget.so",           "py3.6/zjWidget.so"),
                ("py3.6/pageSliceList.so",      "py3.6/pageSliceList.so"),
                ("py3.6/ZJ300.so",              "py3.6/ZJ300.so"),
                ("py3.6/ZJSlideViewer.so",      "py3.6/ZJSlideViewer.so"),
                ("py3.6/srvc_dgns.so",          "py3.6/srvc_dgns.so"),
                ("py3.6/zjViewer.so",           "py3.6/zjViewer.so"),
                ("py3.6/zjPyramid.so",          "py3.6/zjPyramid.so"),
                ("py3.6/zjDef.so",              "py3.6/zjDef.so"),
                ("py3.6/zjTar.so",              "py3.6/zjTar.so"),

                ("py3.7/cfgZJ300.so",           "py3.7/cfgZJ300.so"),
                ("py3.7/srvc_dgns.so",          "py3.7/srvc_dgns.so"),
                ("py3.7/zjWidget.so",           "py3.7/zjWidget.so"),
                ("py3.7/pageSliceList.so",      "py3.7/pageSliceList.so"),
                ("py3.7/ZJ300.so",              "py3.7/ZJ300.so"),
                ("py3.7/ZJSlideViewer.so",      "py3.7/ZJSlideViewer.so"),
                ("py3.7/srvc_dgns.so",          "py3.7/srvc_dgns.so"),
                ("py3.7/zjViewer.so",           "py3.7/zjViewer.so"),
                ("py3.7/zjPyramid.so",          "py3.7/zjPyramid.so"),
                ("py3.7/zjDef.so",              "py3.7/zjDef.so"),
                ("py3.7/zjTar.so",              "py3.7/zjTar.so"),

                ("py3.8/cfgZJ300.so",           "py3.8/cfgZJ300.so"),
                ("py3.8/srvc_dgns.so",          "py3.8/srvc_dgns.so"),
                ("py3.8/zjWidget.so",           "py3.8/zjWidget.so"),
                ("py3.8/pageSliceList.so",      "py3.8/pageSliceList.so"),
                ("py3.8/ZJ300.so",              "py3.8/ZJ300.so"),
                ("py3.8/ZJSlideViewer.so",      "py3.8/ZJSlideViewer.so"),
                ("py3.8/srvc_dgns.so",          "py3.8/srvc_dgns.so"),
                ("py3.8/zjViewer.so",           "py3.8/zjViewer.so"),
                ("py3.8/zjPyramid.so",          "py3.8/zjPyramid.so"),
                ("py3.8/zjDef.so",              "py3.8/zjDef.so"),
                ("py3.8/zjTar.so",              "py3.8/zjTar.so"),
                ],
            ("ZJSlideViewer", "Windows"): [
                ("run_ZJSlideViewer.py",        "run_ZJSlideViewer.py"),
                ("loc.json",                    "loc.json"),
                ("resource",                    "resource"),
                ("qss/pageSliceList.qss",       "qss/pageSliceList.qss"),
                ("qss/ZJ300.qss",               "qss/ZJ300.qss"),
                ("README_ZJSlideViewer.pdf",    "README.pdf"),

                ("py3.8/zjLogger.pyd",          "zjLogger.pyd"),
                ("py3.8/cfgZJ300.pyd",          "cfgZJ300.pyd"),
                ("py3.8/srvc_dgns.pyd",         "srvc_dgns.pyd"),
                ("py3.8/zjWidget.pyd",          "zjWidget.pyd"),
                ("py3.8/pageSliceList.pyd",     "pageSliceList.pyd"),
                ("py3.8/sliceViewer.pyd",       "sliceViewer.pyd"),
                ("py3.8/ZJ300.pyd",             "ZJ300.pyd"),
                ("py3.8/ZJSlideViewer.pyd",     "ZJSlideViewer.pyd"),
                ("py3.8/srvc_dgns.pyd",         "srvc_dgns.pyd"),
                ("py3.8/zjViewer.pyd",          "zjViewer.pyd"),
                ("py3.8/zjPyramid.pyd",         "zjPyramid.pyd"),
                ("py3.8/zjDef.pyd",             "zjDef.pyd"),
                ("py3.8/zjTar.pyd",             "zjTar.pyd"),
                ],
            ("ZJELISPOT", "Linux"): [
                ("run_ZJELISPOT.py",        "run_ZJELISPOT.py"),
                ("ZJELISPOT.desktop",       "ZJELISPOT.desktop"),
                ("install.py",              "install.py"),
                ("loc.json",                "loc.json"),
                ("ZJELISPOT.sql",           "ZJELISPOT.sql"),
                ("reportZJELISPOT.py", "reportZJELISPOT.py"),

                ("resource",                "resource"),
                ("qss/login.qss",           "qss/login.qss"),
                ("qss/pageQC.qss",          "qss/pageQC.qss"),
                ("qss/pageResult.qss",      "qss/pageResult.qss"),
                ("qss/pageScanELISPOT.qss", "qss/pageScanELISPOT.qss"),
                ("qss/pageSetting.qss",     "qss/pageSetting.qss"),
                ("qss/ZJELISPOT.qss",       "qss/ZJELISPOT.qss"),

                ("py3.8/cfgZJELISPOT.so",   "py3.8/cfgZJELISPOT.so"),
                ("py3.8/database.so",       "py3.8/database.so"),
                ("py3.8/common.so",         "py3.8/common.so"),
                ("py3.8/languageTrans.so",  "py3.8/languageTrans.so"),
                ("py3.8/pageScanELISPOT.so","py3.8/pageScanELISPOT.so"),
                ("py3.8/pageQC.so",         "py3.8/pageQC.so"),
                ("py3.8/pageResult.so",     "py3.8/pageResult.so"),
                ("py3.8/pageSetting.so",    "py3.8/pageSetting.so"),
                ("py3.8/pageDeviceQC.so",   "py3.8/pageDeviceQC.so"),
                ("py3.8/pageDataManager.so","py3.8/pageDataManager.so"),
                ("py3.8/qcCommon.so",       "py3.8/qcCommon.so"),
                ("py3.8/reportViewer.so",   "py3.8/reportViewer.so"),
                ("py3.8/ZJELISPOT.so",      "py3.8/ZJELISPOT.so"),
                ("py3.8/zjElispotView.so",  "py3.8/zjElispotView.so"),
                ("py3.8/zjGraphView.so",    "py3.8/zjGraphView.so"),
            ],
            # ("ZJELISPOT", "Windows"): [], # 不支持
            }

    strPlatform = platform.system()
    lst_fr_to = dct_package.get((strPackageType, strPlatform))
    if lst_fr_to is None:
        logger.error("%s on %s has not been supported " % (strPackageType, strPlatform))
        return

    pack_dir = os.path.join(work_dir, strPackageType + "_" + platform.system())

    # 创建打包目录
    if os.path.isfile(pack_dir): os.remove(pack_dir)
    if os.path.isdir(pack_dir): shutil.rmtree(pack_dir)
    os.mkdir(pack_dir)

    for fr_to in lst_fr_to:
        fr, to = fr_to

        fr_pth = os.path.join(work_dir, fr)
        to_pth = os.path.join(pack_dir, to)

        if not os.path.exists(fr_pth):
            logger.error("Invalid file: %s" % fr_pth)
            continue
        if os.path.isfile(fr_pth):
            to_dir = os.path.split(to_pth)[0]
            if not os.path.isdir(to_dir): os.makedirs(to_dir) # 目标文件所在目录如不存在, 则创建
            if os.path.isfile(to_pth): os.remove(to_pth) # 目标文件如存在则删除
            shutil.copy2(fr_pth, to_pth) # 拷贝文件
            if not os.path.isfile(to_pth): # 目标文件需拷贝成功
                logger.error("Fail to copy file, from: %s, to: %s" % (fr_pth, to_pth))
                continue
        elif os.path.isdir(fr_pth):
            to_dir = os.path.split(to_pth)[0]
            if not os.path.isdir(to_dir): os.makedirs(to_dir) # 目标文件所在目录如不存在, 则创建
            if os.path.isfile(to_pth): os.remove(to_pth) # 目标文件如存在则删除
            if os.path.isdir(to_pth): shutil.rmtree(to_pth) # 目标文件如存在则删除
            shutil.copytree(fr_pth, to_pth)
            if not os.path.isdir(to_pth):
                logger.error("Fail to copy file, from: %s, to: %s" % (fr_pth, to_pth))
                continue
        else:
            logger.error("Invalid file, from: %s, to: %s" % (fr_pth, to_pth))
            continue

        """
        zip_dir = os.path.join(pack_dir, os.path.split(pack_dir)[1]) # 重命名文件
        os.rename(dist_dir, zip_dir)

        # 整体打包成 zip
        zip_pth = os.path.join(work_dir, os.path.split(pack_dir)[1] + "_exe.zip")
        if os.path.isfile(zip_pth): os.remove(zip_pth)
        zipDirToZip(zip_dir, zip_pth)

        # 将目录下的文件移到上一级
        pack_dir_tmp = pack_dir + "_tmp"
        if os.path.isfile(pack_dir_tmp): os.remove(pack_dir_tmp)
        if os.path.isdir(pack_dir_tmp): shutil.rmtree(pack_dir_tmp)
        shutil.move(zip_dir, pack_dir_tmp)
        shutil.rmtree(pack_dir)
        shutil.move(pack_dir_tmp, pack_dir)
        """
    if os.path.isdir(pack_dir):
        logger.info("Succed to package[%s, %s], output dir: %s" % (strPackageType, strPlatform, pack_dir))
    else:
        logger.error("Failed to package[%s, %s], output dir: %s" % (strPackageType, strPlatform, pack_dir))

def release(strPackageType):
    dct_package = { # 取值: [(文件名, 是否从工作目录拷贝, 是否拷贝到的打包目录(True: 拷贝到打包目录, False: 拷贝到dist目录)), ]
            ("ZJ300-CS3", "Linux"): [],
            # ("ZJ300-CS3", "Windows"): [], # 不支持
            ("ZJ300-CS3_Report", "Linux"): [],
            ("ZJ300-CS3_Report", "Windows"): [
                ("ZJ300-CS3_Report.spec", True, True),
                ("ZJ300-CS3_Report.nsi", True, False),
                ("wkhtmltopdf", False, False),
                ("poppler", False, False),
                ("reportZJ300.py", False, False),
                ("zjCalendar.py", False, False),
                ("loc.json", False, False),
                ("SimHei.ttf", False, False),
                ("resource", False, False),
                ("qss", False, False),
                ],
            ("ZJ-HST", "Linux"): [],
            # ("ZJ-HST", "Windows"): [], # 不支持
            ("ZJSlideViewer", "Linux"): [],
            ("ZJSlideViewer", "Windows"): [
                ("ZJSlideViewer.spec", True, True),
                ("ZJSlideViewer.nsi", True, False),
                ("README.pdf", False, False),
                ("loc.json", False, False),
                ("qss", False, False),
                ("resource", False, False),
                ],
            ("ZJELISPOT", "Linux"): [],
            # ("ZJELISPOT", "Windows"): [], # 不支持
            }

    strPlatform = platform.system()
    lst_fr = dct_package.get((strPackageType, strPlatform))
    if lst_fr is None:
        logger.error("%s on %s has not been supported " % (strPackageType, strPlatform))
        return

    if strPlatform != "Windows": # 非 Windows 下的安装包发布不由此脚本负责
        return

    pack_dir = os.path.join(work_dir, strPackageType + "_" + platform.system())
    # logger.info("pack_dir: %s" % pack_dir)
    if strPackageType in ["ZJ300-CS3_Report", "ZJSlideViewer", ] and strPlatform == "Windows":
        py_dir = "C:\\pro\\python38" # 只使用 py3.8 打包
        # 拷贝所需文件到 pack_dir 目录
        for fr in lst_fr:
            file_name_full, isFromWorkDir, isToPackDir = fr
            if not isToPackDir: continue
            if isFromWorkDir: fr_pth = os.path.join(work_dir, file_name_full)
            else:             fr_pth = os.path.join(pack_dir, file_name_full)
            to_pth = os.path.join(pack_dir, file_name_full)
            if os.path.isfile(fr_pth): shutil.copyfile(fr_pth, to_pth)
            elif os.path.isdir(fr_pth): shutil.copytree(fr_pth, to_pth)
            else: logger.error("Invalid file path: %s" % fr_pth)

        build_dir = os.path.join(pack_dir, "build")
        dist_dir = os.path.join(pack_dir, "dist")
        if os.path.isdir(build_dir): shutil.rmtree(build_dir)
        if os.path.isdir(dist_dir): shutil.rmtree(dist_dir)

        spec_pth = os.path.join(pack_dir, "%s.spec" % strPackageType)
        if not os.path.isfile(spec_pth):
            logger.error("No file: %s" % spec_pth)
            return

        # 使用 pyinstaller 打包, -D: py3.8 不支持 -D 选项
        str_cmd = '%s\\Scripts\\pyinstaller.exe %s.spec' % (py_dir, strPackageType)
        # logger.info("os.chdir: %s" % pack_dir)
        os.chdir(pack_dir)
        logger.info(str_cmd)
        os.system(str_cmd)
        # logger.info("os.chdir: %s" % work_dir)
        os.chdir(work_dir)
        if not os.path.isdir(dist_dir):
            logger.error("No dist dirctory: %s" % dist_dir)
            return

        dist_work_dir = os.path.join(dist_dir, strPackageType)
        if not os.path.isdir(dist_work_dir):
            logger.error("No such directory: %s" % dist_work_dir)
            return

        # 拷贝所需文件到 dist/strPackageType 目录
        for fr in lst_fr:
            file_name_full, isFromWorkDir, isToPackDir = fr
            if isToPackDir: continue
            if isFromWorkDir: fr_pth = os.path.join(work_dir, file_name_full)
            else:             fr_pth = os.path.join(pack_dir, file_name_full)
            to_pth = os.path.join(dist_work_dir, file_name_full)
            if os.path.isfile(to_pth): os.remove(to_pth) # 删除历史残留文件
            elif os.path.isdir(to_pth): shutil.rmtree(to_pth) # 删除历史残留文件
            if os.path.isfile(fr_pth): shutil.copyfile(fr_pth, to_pth)
            elif os.path.isdir(fr_pth): shutil.copytree(fr_pth, to_pth)
            else: logger.error("Invalid file path: %s" % fr_pth)

        # 补充所需要文件到 dist_work_dir 目录
        lst_lib_dir = [
                "lib2to3",
                "site-packages\\scipy",
                "site-packages\\matplotlib",
                "site-packages\\sklearn",
                "site-packages\\numpy",
                "site-packages\\certifi",
                "site-packages\\cv2",
                "site-packages\\PIL",
                "site-packages\\PyQt5",
                "site-packages\\pdf2image",
                "site-packages\\pdfkit",
                # tcl", # tcl 不属于 Lib 目录下
                "site-packages\\tornado",    # python3 -m pip install tornado
                "site-packages\\win32com",   # python3 -m pip install pypiwin32
                "site-packages\\kiwisolver", # python3 -m pip install kiwisolver
                "site-packages\\xlwt",
                ]
        for lib_dir in lst_lib_dir:
            fr_dir = os.path.join(py_dir + "\\Lib", lib_dir)
            lib_dir_tmp = lib_dir.replace("\\", "/")
            to_dir = os.path.join(dist_work_dir, os.path.split(lib_dir_tmp)[1])
            if os.path.isdir(to_dir): shutil.rmtree(to_dir)
            elif os.path.isfile(to_dir): os.remove(to_dir)
            if os.path.isfile(fr_dir): pass
            elif os.path.isdir(fr_dir): pass
            else:
                logger.error("No file or directory: %s" % fr_dir)
                continue
            if os.path.isfile(fr_dir):
                shutil.copy2(fr_dir, to_dir)
                if not os.path.isfile(to_dir):
                    logger.error("Failed to copy to file: %s" % to_dir)
            else:
                shutil.copytree(fr_dir, to_dir)
                if not os.path.isdir(to_dir):
                    logger.error("Failed to copy to directory: %s" % to_dir)

        setup_pth_fr = os.path.join(dist_work_dir, "%sSetup.exe" % strPackageType)
        setup_pth_to = os.path.join(work_dir, "%sSetup.exe" % (strPackageType))
        if os.path.isfile(setup_pth_to): os.remove(setup_pth_to)

        nsi_pth_fr = os.path.join(work_dir, '%s.nsi' % strPackageType)
        nsi_pth_to = os.path.join(dist_work_dir, '%s.nsi' % strPackageType)
        if os.path.isfile(nsi_pth_to): os.remove(nsi_pth_to)
        shutil.copy2(nsi_pth_fr, nsi_pth_to)

        nsi_pth = os.path.join(dist_work_dir, "%s.nsi" % strPackageType)
        if not os.path.isfile(nsi_pth):
            logger.error("No file: %s" % nsi_pth)
            return

        if True: # 修改 nsi 文件
            # 遍历 dist_work_dir 下所有目录, 构建打包列表
            lst_add, lst_del = [], []
            for root, dirs, files in os.walk(dist_work_dir):
                for dir_name in dirs:
                    dir_pth = os.path.join(root, dir_name)
                    if dir_pth.find("__pycache__") != -1: continue

                    pos = dir_pth.find(dist_work_dir)
                    if pos == -1:
                        logger.error("dist_work_dir: %s, dir_pth: %s" \
                                % (dist_work_dir, dir_pth))
                        continue
                    dir_part = dir_pth[pos + len(dist_work_dir) + 1: ]

                    bFoundFile = False
                    lst_dir_or_file = os.listdir(dir_pth)
                    for dir_or_file in lst_dir_or_file:
                        dir_or_file_pth = os.path.join(dir_pth, dir_or_file)
                        if os.path.isfile(dir_or_file_pth):
                            bFoundFile = True
                            break
                    if bFoundFile: # 目录下有文件才添加
                        strLine = '  SetOutPath "$INSTDIR\\%s"\n' % dir_part
                        strLine = strLine.replace("/", "\\")
                        lst_add.append(strLine)

                        strLine = '  File %s\\*\n' % dir_part
                        strLine = strLine.replace("/", "\\")
                        lst_add.append(strLine)

                    strLine = '  Delete $INSTDIR\\%s\*\n' % dir_part
                    strLine = strLine.replace("/", "\\")
                    lst_del.append(strLine)

                    strLine = '  RMDir "$INSTDIR\\%s"\n' % dir_part
                    strLine = strLine.replace("/", "\\")
                    lst_del.append(strLine)

            with open(nsi_pth, "r", encoding="gb2312") as f: # utf-8
                txtList = f.readlines()

            txtList_new = []
            for txtLine in txtList:
                txtList_new.append(txtLine)
                if txtLine.find("ADD_DIR_BGN") != -1:
                    txtList_new.extend(lst_add)
                if txtLine.find("DEL_DIR_BGN") != -1:
                    # NOTE: 删除有先后次序要求, 故多执行几遍以确保删除
                    for _ in range(0, 20): txtList_new.extend(lst_del)

            with open(nsi_pth, "w", encoding="gb2312") as f: # utf-8
                f.writelines(txtList_new)
                f.close()

        os.chdir(dist_work_dir)
        str_cmd = '"C:\\Program Files (x86)\\NSIS\\makensis.exe" %s.nsi' % strPackageType
        logger.info(str_cmd)
        os.system(str_cmd)
        os.chdir(work_dir)

        if not os.path.isfile(setup_pth_fr):
            logger.error("No file: %s" % setup_pth_fr)
        else:
            shutil.move(setup_pth_fr, setup_pth_to)

        # 删除打包临时目录
        # shutil.rmtree(build_dir) # NOTE: 暂时不删除以便定位问题
        # shutil.rmtree(dist_dir) # NOTE: 暂时不删除以便定位问题

        if os.path.isfile(setup_pth_to):
            logger.info("Succed to release[%s, %s], output file: %s" % (strPackageType, strPlatform, setup_pth_to))
        else:
            logger.info("Failed to release[%s, %s], output file: %s" % (strPackageType, strPlatform, setup_pth_to))

def encrypt(strPackageType):
    # 诊断目录中zjNet ZJMisc无法加密
    logger.info("%s, start encrypt" % strPackageType)
    strPlatform = platform.system()
    if strPlatform == 'Windows':
        if strPackageType not in ["ZJ300-CS3_Report", "ZJSlideViewer", ]:
            printHelp()
            return

        os.chdir(work_dir)
        package_prjx_path = os.path.join("prjx", f"{strPackageType}.prjx")

        # exe 文件加密
        # C:\Program Files (x86)\Gemalto Sentinel\Sentinel LDK\VendorTools\VendorSuite\envelope.com 添加至环境变量Path
        # 执行加密,exe文件名后添加crypt，表示加密后的exe文件(无法覆盖，加密后再移动、重命名)
        if os.path.isfile(package_prjx_path):
            os.system('envelope.com -p {}'.format(package_prjx_path))
        else:
            logger.error("%s, Fail to encrypt, no file: %s" % (strPackageType, package_prjx_path))
            return

        # 替换 pyd *.pyd.crypt --> *.pyd
        nCryptSucced, nCryptFailed = 0, 0
        targetDir = '{}_Windows'.format(strPackageType)
        for f in os.listdir(targetDir):
            baseF, ext = os.path.splitext(f)
            if ext == '.crypt':
                package_path_ori = os.path.join(targetDir, baseF)
                package_path_crypt = os.path.join(targetDir, f)
                if os.path.isfile(package_path_crypt) and os.path.isfile(package_path_ori):
                    os.remove(package_path_ori)
                    os.rename(package_path_crypt, package_path_ori)
                    logger.info("Succeed to crypt[{}, {}], output file: {}".format(strPackageType, strPlatform, package_path_ori))
                    nCryptSucced += 1
                else:
                    logger.error("Failed to crypt[{}, {}], output file: {}".format(strPackageType, strPlatform, package_path_ori))
                    nCryptFailed += 1
        logger.info("%s, nCryptSucced: %d, nCryptFailed: %d" % (strPackageType, nCryptSucced, nCryptFailed))

    else:
        SENTINEL_BASE_PATH_LINUX = "/home/manager/prj/ZJ_RD/09_发布/Sentinel"
        linuxEnv_absPath = os.path.join(SENTINEL_BASE_PATH_LINUX, "VendorTools", "Envelope", "linuxenv")
        crypt_hvc_absPath = os.path.join(SENTINEL_BASE_PATH_LINUX, "crypt.hvc")

        if strPackageType not in ["ZJ-HST", "ZJ300-CS3", "ZJ300-CS3_Report", "ZJSlideViewer", "ZJELISPOT", ]:
            printHelp()
            return
        else:
            package_root_path = os.path.join(work_dir, strPackageType + "_Linux")

            for root, dirs, files in os.walk(package_root_path):
                for dir_ in dirs:
                    fileDir = os.path.join(root,dir_)
                    #pyVer = "py" + platform.python_version()[:3]

                    # 加密对应python环境的so, 默认全加密
                    if dir_ in ["py3.6", "py3.7", "py3.8"]:
                        lst_so_file = os.listdir(fileDir)
                        for soFile in lst_so_file:
                            if os.path.splitext(soFile)[-1] != ".so": continue
                            absFilePath = os.path.join(fileDir, soFile)
                            fileCryptCMD = "{0} -f:0 -v:{1} {2} {2}".format(linuxEnv_absPath, crypt_hvc_absPath, absFilePath)
                            os.system(fileCryptCMD)
        logger.info("end crypt !!!")

def main():
    # 设置日志文件名
    if len(sys.argv) >= 2 and sys.argv[1] in ["-help", "-lockRepository", "-unlockRepository", \
            "-compile", "-package", "-encrypt", "-release", ]:
        strProductName = "ZJELISPOT"
        strSystem = sys.argv[1][1: ]
        th = logging_handles.TimedRotatingFileHandler( # 输出到文件中
                filename=strProductName + "_" + strSystem + ".log",
                when='D', # 间隔的时间单位, # S 秒, M 分, H 小时, D 天, W 每星期(interval == 0 时代表星期一), midnight 每天凌晨
                backupCount=3, # 备份文件的个数, 如果超过这个个数，就会自动删除
                encoding='utf-8') # 往文件里写入
        th.setFormatter(logging.Formatter("[%(asctime)s] [%(levelname)s] [%(module)s] [%(funcName)s] [%(lineno)s] %(message)s")) # 设置文件里写入的格式
        th.suffix = "%Y%m%d"
        logger.addHandler(th)

    if len(sys.argv) == 1:
        printHelp()
        return
    if sys.argv[1] == "-help":
        printHelp()
        return

    if sys.argv[1] == "-lockRepository":
        lockRepository()
        return

    if sys.argv[1] == "-unlockRepository":
        unlockRepository()
        return

    if sys.argv[1] == "-release":
        if len(sys.argv) != 3:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        strPackageType = sys.argv[2]

        if strPackageType not in ["ZJ-HST", "ZJ300-CS3", "ZJ300-CS3_Report", "ZJSlideViewer", "ZJELISPOT", ]:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return

        release(strPackageType)
        return

    if sys.argv[1] == "-encrypt":
        if len(sys.argv) != 3:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        strPackageType = sys.argv[2]

        if strPackageType not in ["ZJ-HST", "ZJ300-CS3", "ZJ300-CS3_Report", "ZJSlideViewer", "ZJELISPOT", ]:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        encrypt(strPackageType)
        return

    if sys.argv[1] == "-package":
        if len(sys.argv) != 3:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        strPackageType = sys.argv[2]

        if strPackageType not in ["ZJ-HST", "ZJ300-CS3", "ZJ300-CS3_Report", "ZJSlideViewer", "ZJELISPOT", ]:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        package(strPackageType)
        return

    if sys.argv[1] == "-compile":
        if len(sys.argv) not in [3, 4]:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        if sys.argv[2] not in ["3.6", "3.7", "3.8"]:
            print("Invalid options: %s" % str(sys.argv))
            printHelp()
            return
        PY_VER_FULL = sys.argv[2]
        PY_VER_FULL_NODOT = sys.argv[2].replace(".", "")
        if len(sys.argv) == 4:

            if os.path.splitext(sys.argv[3])[1] != ".py":
                print("Invalid options: %s" % str(sys.argv))
                printHelp()
                return
            if not os.path.isfile(sys.argv[3]):
                print("文件不存在: %s" % sys.argv[3])
                printHelp()
                return
            lst_file = [sys.argv[3], ]
        else:
            lst_file = None

        # if platform.system() == "Windows":
        #     if PY_VER_FULL != "3.8":
        #         print("Only python3.8 supported on Windows")
        #         return
        compilePy(PY_VER_FULL, PY_VER_FULL_NODOT, lst_file)
        return

    print("Invalid options: %s" % str(sys.argv))
    printHelp()
    return

if __name__ == '__main__':
    main()
